﻿using Core.Framework;
using iTool.ClusterComponent;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyModel;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace iTool.PluginCore
{
    /// <summary>
    /// 插件管理器
    /// </summary>
    /// var assembly = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "/Plugins/" + dll);
    public class ServerPluginsManager
    {

        Dictionary<string, ServerPluginSchema> PluginsMap = new Dictionary<string, ServerPluginSchema>();
        public IConfiguration Configuration { get; }

        static ServerPluginsManager()
        {
            var platform = Environment.OSVersion.Platform.ToString();
            var runtimeAssemblyNames = DependencyContext.Default.GetRuntimeAssemblyNames(platform);
            foreach (var asslembyNames in runtimeAssemblyNames)
            {
                if (asslembyNames.Name.StartsWith("Plugin."))
                {
                    Assembly.Load(asslembyNames);
                }
            }
        }

        public ServerPluginsManager(IConfiguration configuration)
        {
            Configuration = configuration;
            this.LoadPlugins();
        }

        private void LoadPlugins()
        {
            try
            {

                PluginsMap = new Dictionary<string, ServerPluginSchema>();
                // 加载过早 AppDomain.CurrentDomain 获取数据 不完整

                Print.Line("\tiTool> Load plugins starting...", ConsoleColor.Cyan);

                this.LoadCurrentDomainPlugins();

                IEnumerable<FileInfo> files = new List<FileInfo>();

                // 动态加载插件 Plugins
                GetLocalPluginFilePaths("Plugins/", ref files);

                var paths = files.Select(a => a.FullName).ToList();

                foreach (var dllPath in paths)
                {
                    var assembly = Assembly.LoadFile(dllPath);
                    var types = assembly.GetTypes().Where(a => a.GetInterfaces().Contains(typeof(ILServerPlugin)));
                    this.AddPluginInstancesOfTypes(types);
                }

                Console.ForegroundColor = ConsoleColor.DarkGreen;
                Print.Line("\tiTool> Load plugins success!", ConsoleColor.Cyan);
                Console.ForegroundColor = ConsoleColor.White;

                this.BuildeLocalCallFile();
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Print.Error($"iTool> Load plugins error!, messgae:{ex.Message}\n");
                Console.ForegroundColor = ConsoleColor.White;
                throw;
            }
        }
        private void BuildeLocalCallFile()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendLine("const axios = require('axios');");
            stringBuilder.AppendLine("var server = 'http://localhost:17010';");

            stringBuilder.AppendLine("function _getPluginParameters(pluginName,exportMethodName, parameters) {");
            stringBuilder.AppendLine("\treturn {\"pluginName\": pluginName,\"exportMethodName\": exportMethodName,\"parameters\":parameters};");
            stringBuilder.AppendLine("}");



            stringBuilder.AppendLine("module.exports = {");


            Dictionary<string, List<string>> keyValues = new Dictionary<string, List<string>>();

            foreach (var item in PluginsMap)
            {
                stringBuilder.AppendLine($"\t{item.Key}:{{");

                //keyValues.Add(item.Key, item.Value.ExportMethods.Select(a => a.Key).ToList());
                foreach (var method in item.Value.ExportMethods)
                {
                    stringBuilder.AppendLine($"\t\t{method.Key}: async function() {{");
                    stringBuilder.AppendLine("\t\t\tvar parameters = [];if (arguments) {for(var i in arguments){parameters.push(arguments[i])}};");
                    stringBuilder.AppendLine($"\t\t\tvar result = await axios.post(server, _getPluginParameters(\"{ item.Key}\",\"{method.Key}\", parameters));return result.data;");
                    stringBuilder.AppendLine("\t\t},");
                }

                stringBuilder.AppendLine("\t},");
            }

            stringBuilder.AppendLine("}");

            File.WriteAllText("./pluginsInfo.js", stringBuilder.ToString());
        }
        private void AddPluginInstancesOfTypes(IEnumerable<Type> types)
        {
            foreach (var type in types)
            {
                var value = new ServerPluginSchema();

                {
                    Type[] constractorParameterTypes = new Type[0];
                    object[] parametes = new object[0];
                    ParameterInfo[] constractorParameter = type.GetConstructors().Single().GetParameters();
                    if (constractorParameter.Count() > 0)
                    {
                        constractorParameterTypes = constractorParameter.Select(a => a.ParameterType).ToArray();
                        parametes = new object[constractorParameterTypes.Length];
                        for (int i = 0; i < constractorParameterTypes.Count(); i++)
                        {
                            parametes[i] = constractorParameterTypes[i] == typeof(IConfiguration) ? this.Configuration : ProviderOfServices.GetService(constractorParameterTypes[i], "PluginsProvider");
                        }
                    }

                    //  创建的实例
                    value.Instance = Activator.CreateInstance(type, parametes);
                }

                // 获取所有导出的方法
                MethodInfo[] methods = type.GetMethods().Where(t => t.GetCustomAttribute<ExportMethodAttribute>() != null).ToArray();

                // 执行任何方法
                foreach (var methodInfo in methods)
                {
                    ExportMethodAttribute exportMethodAttribute = methodInfo.GetCustomAttribute<ExportMethodAttribute>();

                    var methodEntity = new Method();
                    methodEntity.methodInfo = methodInfo;
                    methodEntity.ParametersTypes = methodInfo.GetParameters().Select(a => a.ParameterType).ToArray();
                    methodEntity.IsAsync = methodInfo.IsDefined(typeof(AsyncStateMachineAttribute), false);
                    methodEntity.IsTaskDynamic = methodInfo.ReturnType == typeof(Task<dynamic>);
                    value.ExportMethods.Add(exportMethodAttribute.MethodName ?? methodInfo.Name, methodEntity);
                }

                var interfaceName = ((ILServerPlugin)value.Instance).Name;

                // 键入插件集合
                PluginsMap.Add(interfaceName, value);
            }
        }
        private void LoadCurrentDomainPlugins()
        {
            var types = this.GetPluginsByInterface<ILServerPlugin>();
            Print.Line($"\tiTool> Create plugins count:{types.Length}.", ConsoleColor.Cyan);
            this.AddPluginInstancesOfTypes(types);
        }

        /// <summary>
        /// 获取本地所有的插件包
        /// </summary>
        /// <returns></returns>
        private void GetLocalPluginFilePaths(string path, ref IEnumerable<FileInfo> files)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
                return;
            }

            string[] dirs = Directory.GetDirectories(path);

            DirectoryInfo dirInfo = new DirectoryInfo(path);
            FileInfo[] subFiles = dirInfo.GetFiles() ?? new FileInfo[0];


            files = files.Concat(subFiles.Where(a =>
                a.Extension == ".dll"
                && a.Name != "iTool.PluginCore.dll"
            ));

            //var files = subFiles.Where(a => 
            //    a.Extension == ".dll" 
            //    && a.Name != "iTool.PluginCore.dll"
            //).Select(a => a.Name).ToList();

            if (dirs != null)
            {
                foreach (string dir in dirs)
                {
                    GetLocalPluginFilePaths(dir, ref files);
                }
            }

        }

        /// <summary>
        /// 执行方法
        /// </summary>
        /// <param name="pluginName">插件名称</param>
        /// <param name="methodName">导出方法名称</param>
        /// <param name="parameters">参数</param>
        /// <returns></returns>
        public async Task<dynamic> InvokeAsync(string pluginName, string exportMethodName, params object[] parameters)
        {
            if (PluginsMap.ContainsKey(pluginName))
            {
                var plugin = PluginsMap[pluginName];

                return await plugin.InvokeAsync(exportMethodName, parameters);
            }

            throw new Exception($"未知 methodName : {pluginName}");
        }


        private Type[] GetPluginsByInterface<T>()
            where T : class
        {
            return AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(a => a.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(T))))
                .ToArray();
        }

    }


    public class ServerPluginSchema
    {
        /// <summary>
        /// 创建的实例
        /// </summary>
        public object Instance { get; set; }

        /// <summary>
        /// 导出的方法集合
        /// </summary>
        public Dictionary<string, Method> ExportMethods { get; set; } = new Dictionary<string, Method>();

        public async Task<dynamic> InvokeAsync(string methodName, params object[] input)
        {
            if (ExportMethods.ContainsKey(methodName))
            {
                var method = ExportMethods[methodName];

                var parameters = new object[method.ParametersTypes.Count()];

                for (int i = 0; i < method.ParametersTypes.Count(); i++)
                {
                    if (input.Length <= i)
                    {
                        break;
                    }

                    if (method.ParametersTypes[i] == typeof(string))
                    {
                        parameters[i] = input[i];
                    }
                    else
                    {
                        parameters[i] = JsonConvert.DeserializeObject(input[i].ToString(), method.ParametersTypes[i]);
                    }
                }

                if (method.IsAsync || method.IsTaskDynamic)
                {
                    //Type? returnType = method.methodInfo.ReturnType;

                    //var xxx1 = Convert.ChangeType("", method.methodInfo.ReturnType);

                    return await (dynamic)method.methodInfo.Invoke(this.Instance, parameters);

                    //return await (Task<dynamic>)method.methodInfo.Invoke(this.Instance, parameters);
                }
                else
                {
                    return (dynamic)method.methodInfo.Invoke(this.Instance, parameters);
                }
            }

            throw new Exception($"未知 methodName : {methodName}");
        }
    }

    public class Method
    {
        /// <summary>
        ///  方法对象
        /// </summary>
        public MethodInfo methodInfo { get; set; }

        /// <summary>
        /// 是否是异步
        /// </summary>
        public bool IsAsync { get; set; }

        /// <summary>
        /// 是否是Task
        /// </summary>
        public bool IsTaskDynamic { get; set; }

        /// <summary>
        /// 参数类型集合
        /// </summary>
        public Type[] ParametersTypes { get; set; }
    }


    public class PluginRequestBodyParameter
    {
        public string PluginName { get; set; }
        public string ExportMethodName { get; set; }
        public object[] Parameters { get; set; }
    }
}
